/**
 *  Project: CloudstickUtils 
 *  kr.cloudstick.util.crypto-CSSha256.java
 * 	
 *	Copyright 2013 Cloudstick (www.cloudstick.kr)
 *
 *	Written by htkim
 *		2013. 3. 27. first written.
 *
 *	Note:	
 */
package kr.cloudstick.util.crypto;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;

import kr.cloudstick.util.CSException;

public final class CSSha256 {

	private final static int SALT_LENGTH = 8;
	private final static String ENCODING = "UTF-8";
	private final static String SALT_ALGORITHM = "SHA1PRNG";
	private final static String DIGEST_ALGORITHM = "SHA-256";
	
	public final static int DEFAULT_ITERARION = 1000;
	public final static int MAX_ITERATION = 10000;
	
	private CSSha256() {
		throw new AssertionError();
	}
	
	public static byte[] getSalt() {
		try {
			final SecureRandom random = SecureRandom.getInstance(SALT_ALGORITHM);
			final byte[] salt = new byte[SALT_LENGTH];
			random.nextBytes(salt);
			return salt;
		} catch(NoSuchAlgorithmException e) {
			throw new CSException("Invalid algorithm[" + SALT_ALGORITHM + "]", e); // NOPMD by cloudstick on 13. 3. 28 5:08
		}
	}
	
	public static byte[] getHash(final String password) 
	{
		try {
			final MessageDigest digest = MessageDigest.getInstance(DIGEST_ALGORITHM);
			digest.reset();
			return digest.digest(password.getBytes(ENCODING));
		} catch (NoSuchAlgorithmException e) {
			throw new CSException("Invalid algorithm[" + DIGEST_ALGORITHM + "]", e);
		} catch (UnsupportedEncodingException e) {
			throw new CSException("Error while obtaining sha hash values.", e);
		}
	}
	
	public static byte[] getHash(final String password, final byte[] salt)
	{
		try {
			final MessageDigest digest = MessageDigest.getInstance(DIGEST_ALGORITHM);
			digest.reset();
			digest.update(salt);
			return digest.digest(password.getBytes(ENCODING));
		} catch (NoSuchAlgorithmException e) {
			throw new CSException("Invalid algorithm[" + DIGEST_ALGORITHM + "]", e);
		} catch (UnsupportedEncodingException e) {
			throw new CSException("Error while obtaining sha hash values.", e);
		}
	}
	
	public static byte[] getHash(final int iterationNb, final String password, final byte[] salt)
	{
		if ( iterationNb > MAX_ITERATION )
		{
			throw new CSException("Invalid iteration[" + iterationNb + "]");
		}
		try {
			final MessageDigest digest = MessageDigest.getInstance(DIGEST_ALGORITHM);
			digest.reset();
			digest.update(salt);
			byte[] input = digest.digest(password.getBytes(ENCODING)); // NOPMD by cloudstick on 13. 3. 28 5:08
			for(int i=0; i<iterationNb; i++)
			{
				digest.reset();
				input = digest.digest(input);
			}
			return input;
		} catch (NoSuchAlgorithmException e) {
			throw new CSException("Invalid algorithm[" + DIGEST_ALGORITHM + "]", e);
		} catch (UnsupportedEncodingException e) {
			throw new CSException("Error while obtaining sha hash values.", e);
		}	
	}
	
	public static boolean verify(final int iterationNb, 
								final String password, 
								final String digest, 
								final String salt) {
		final byte[] bDigest = CSCodec.base64ToByte(digest);
		final byte[] bSalt = CSCodec.base64ToByte(salt);
		final byte[] proposedDigest = getHash(iterationNb, password, bSalt);
		return Arrays.equals(proposedDigest, bDigest);
	}
}
